//: [Previous](@previous)

import Foundation

/*:
 
 * 方法是与某些特定类型相关联的函数。
 
 * 类、结构体、枚举都可以定义实例方法；实例方法为给定类型的实例封装了具体的任务与功能。
 
 * 类、结构体、枚举也可以定义类型方法；类型方法与类型本身相关联。
 
 * 结构体和枚举能够定义方法是 Swift 与 C/Objective-C 的主要区别之一。
 
 */

/*:
 #### 实例方法
 
 * 实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。一般情况下不需要使用self，这是和OC不同的地方。
 
 *  实例方法的某个参数名称与实例的某个属性名称相同的时候，参数名称享有优先权。这时你可以使用 self 属性来区分参数名称和属性名称。
 
 * 结构体和枚举是值类型。默认情况下，值类型的属性不能在它的实例方法中被修改。如果你确实需要在某个特定的方法中修改结构体或者枚举的属性，你可以为这个方法选择 可变（mutating）行为，将关键字 mutating 放到方法的 func 关键字之前就可以了。
 
 *  枚举的可变方法可以把 self 设置为同一枚举类型中不同的成员：
 
 */

class Counter {
    var count = 0
    func increment() {
        count += 1
    }
    func increment(by amount: Int) {
        count += amount
    }
    func reset() {
        count = 0
    }
}

let counter = Counter()
// 初始计数值是0
counter.increment()
// 计数值现在是1
counter.increment(by: 5)
// 计数值现在是6
counter.reset()
// 计数值现在是0

struct Point {
    var x = 0.0, y = 0.0
    
    func isToTheRightOf(x: Double) -> Bool {
        // self 消除方法参数 x 和实例属性 x 之间的歧义
        return self.x > x
    }
    
    // 改变结构体的属性用mutating关键字
    mutating func moveBy(x deltaX: Double, y deltaY: Double) {
            x += deltaX
            y += deltaY
        }
}
var somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
    print("This point is to the right of the line where x == 1.0")
}
// 打印“This point is to the right of the line where x == 1.0”

somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印“The point is now at (6.0, 8.0)”

enum TriStateSwitch {
    case off, low, high
    mutating func next() {
        switch self {
        case .off:
            self = .low
        case .low:
            self = .high
        case .high:
            self = .off
        }
    }
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight 现在等于 .high
ovenLight.next()
// ovenLight 现在等于 .off

/*:
 #### 类型方法
 
 * 在方法的 func 关键字之前加上关键字 static，来指定类型方法。类还可以用关键字 class 来指定，从而允许子类重写父类该方法的实现。(class是类专用的)（优先使用static）
 
 * 类型方法和实例方法一样用点语法调用。但是，你是在类型上调用这个方法，而不是在实例上调用。
 
 * 在类型方法的方法体（body）中，self 属性指向这个类型本身，而不是类型的某个实例。这意味着你可以用 self 来消除类型属性和类型方法参数之间的歧义（类似于我们在前面处理实例属性和实例方法参数时做的那样）。
 
 * 一般来说，在类型方法的方法体中，任何未限定的方法和属性名称，可以被本类中其他的类型方法和类型属性引用。一个类型方法可以直接通过类型方法的名称调用本类中的其它类型方法，而无需在方法名称前面加上类型名称。类似地，在结构体和枚举中，也能够直接通过类型属性的名称访问本类中的类型属性，而不需要前面加上类型名称。
 
 */

struct LevelTracker {
    static var highestUnlockedLevel = 1
    var currentLevel = 1

    static func unlock(_ level: Int) {
        // 能区分清楚的情况下，highestUnlockedLevel不需要加LevelTracker.这样的限定
        if level > highestUnlockedLevel { highestUnlockedLevel = level }
    }

    static func isUnlocked(_ level: Int) -> Bool {
        return level <= highestUnlockedLevel
    }

    @discardableResult
    mutating func advance(to level: Int) -> Bool {
        if LevelTracker.isUnlocked(level) {
            currentLevel = level
            return true
        } else {
            return false
        }
    }
}

class Player {
    var tracker = LevelTracker()
    let playerName: String
    func complete(level: Int) {
        LevelTracker.unlock(level + 1)
        tracker.advance(to: level + 1)
    }
    init(name: String) {
        playerName = name
    }
}

var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// 打印“highest unlocked level is now 2”

player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
    print("player is now on level 6")
} else {
    print("level 6 has not yet been unlocked")
}
// 打印“level 6 has not yet been unlocked”

//: [Next](@next)
